﻿#region

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using Imdb;
using MediaSage.Lib.Configuration;
using MediaSage.Lib.Database;
using MediaSage.Lib.Utilities;

#endregion

namespace MediaSage.Lib.Plugin.IMDBProvider
{
    public class IMDBProvider : PluginBase
    {
        #region Fields

        private MoviesResultset foundMoviesResultSet;
        private Services imdbAPI;
        private Movie parsedMovie;
        private bool waitingForFoundMovies;
        private bool waitingForMovieParsed;

        #endregion Fields

        #region Constructors

        public IMDBProvider(Config Configuration, PluginEntity PE)
            : base(Configuration, PE)
        {
            imdbAPI = new Services();
            imdbAPI.FoundMovies += foundMovies;
            imdbAPI.MovieParsed += movieParsed;
            logger.Log(strings.INIT_STRING);
        }

        #endregion Constructors

        #region Methods

        public override bool Run(MediaClass mediaObject)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public override IList<PersonEntity> SearchPersonByName(string name)
        {
            throw new NotImplementedException();
        }

        public override string[] About()
        {
            VersionInfo verInfo = GetVersionInfo();
            ;
            string[] returnValue =
                {
                    "IMDBProvider v" + verInfo.MajorVersion + "." + verInfo.MinorVersion + "." +
                    verInfo.BuildNumber
                };
            return returnValue;
        }

        public override IList<MediaFileTypesEntity> ExtensionsHandled()
        {
            throw new NotImplementedException();
        }

        public override PluginEntity GetConfiguration()
        {
            throw new NotImplementedException();
        }

        public override MediaClass GetMedia(MediaClass inputMedia, MediaMergeType mergeType)
        {
            throw new NotImplementedException();
        }

        public override MediaClass GetMediaByID(object ID)
        {
            logger.Log(strings.EnteringSearchForMediaByName + "(" + ID + ")");
            if (imdbAPI == null)
            {
                imdbAPI = new Services();
            }

            return (new MediaClass(config));
        }

        public override MediaClass GetUpdateForMedia(MediaClass inputMedia, MediaMergeType MergeType)
        {
            throw new NotImplementedException();
        }

        public override VersionInfo GetVersionInfo()
        {
            VersionInfo verInfo;
            verInfo.Name = Assembly.GetExecutingAssembly().GetName().Name;
            verInfo.MajorVersion = Assembly.GetExecutingAssembly().GetName().Version.Major;
            verInfo.MinorVersion = Assembly.GetExecutingAssembly().GetName().Version.Minor;
            verInfo.BuildNumber = Assembly.GetExecutingAssembly().GetName().Version.Revision;
            return verInfo;
        }

        public override bool Run()
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMedia(MediaClass mediaObject)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMedia(MediaClass mediaObject, int maxReturns)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMediaByName(string name)
        {
            logger.Log(strings.EnteringSearchForMediaByName + "(" + name + ")");
            if (imdbAPI == null)
            {
                imdbAPI = new Services();
            }
            var returnValue = new List<MediaSearchReturn>();

            // Start the search process
            findMovieWrapper(name);

            foreach (Movie movie in foundMoviesResultSet.ExactMatches)
            {
                var returnObject = new MediaSearchReturn();
                returnObject.IMDBID = "tt" + movie.Id;
                returnObject.name = movie.Title;
                returnObject.year = movie.Year;
                logger.Log(strings.EXACT_MATCH_FOUND + " " + returnObject.name + " (" + returnObject.year + ")");
                float editDistance = Strings.ComputeStringDistance(returnObject.name.Replace("\"", string.Empty), name);
                float pct = 100 - (editDistance * 5);
                if (pct < 0)
                {
                    pct = 0;
                }
                returnObject.pctLikelyMatch = pct;

                returnValue.Add(returnObject);
            }

            if (foundMoviesResultSet.PartialMatches != null)
            {
                foreach (Movie movie in foundMoviesResultSet.PartialMatches)
                {
                    var returnObject = new MediaSearchReturn();
                    returnObject.IMDBID = "tt+" + movie.Id;
                    returnObject.name = movie.Title;
                    returnObject.year = movie.Year;

                    // We will figure 90-(5*edit distance from full name) for the partial score
                    returnObject.pctLikelyMatch = 80 - (Strings.ComputeStringDistance(name, movie.Title) * 5);
                    if (returnObject.pctLikelyMatch < 0)
                    {
                        returnObject.pctLikelyMatch = 0;
                    }
                    returnValue.Add(returnObject);
                    logger.Log(strings.PARTIAL_MATCH_FOUND + " " + movie.Title + " (" + movie.Year + ") %" +
                               returnObject.pctLikelyMatch);
                    returnValue.Add(returnObject);
                }
            }

            logger.Log(strings.ExitingSearchForMediaByName + "(" + name + ")");
            return (returnValue);
        }

        public override List<MediaSearchReturn> SearchForMediaByName(string name, int maxReturns)
        {
            logger.Log(strings.EnteringSearchForMediaByName + "(" + name + "," + maxReturns + ")");
            if (imdbAPI == null)
            {
                imdbAPI = new Services();
            }
            var returnValue = new List<MediaSearchReturn>();

            // Start the search process
            findMovieWrapper(name);
            if (foundMoviesResultSet.ExactMatches != null)
            {
                foreach (Movie movie in foundMoviesResultSet.ExactMatches)
                {
                    var returnObject = new MediaSearchReturn();
                    returnObject.IMDBID = "tt" + movie.Id;
                    returnObject.name = movie.Title;
                    returnObject.year = movie.Year;
                    logger.Log(strings.EXACT_MATCH_FOUND + " " + returnObject.name + " (" + returnObject.year + ")");
                    returnObject.pctLikelyMatch = 90;
                    returnValue.Add(returnObject);
                }
            }
            if (foundMoviesResultSet.PartialMatches != null)
            {
                foreach (Movie movie in foundMoviesResultSet.PartialMatches)
                {
                    var returnObject = new MediaSearchReturn();
                    returnObject.IMDBID = "tt+" + movie.Id;
                    returnObject.name = movie.Title;
                    returnObject.year = movie.Year;

                    // We will figure 90-(2*edit distance from full name) for the partial score
                    returnObject.pctLikelyMatch = 80 - (Strings.ComputeStringDistance(name, movie.Title) * 2);
                    if (returnObject.pctLikelyMatch < 0)
                    {
                        returnObject.pctLikelyMatch = 0;
                    }
                    returnValue.Add(returnObject);
                    logger.Log(strings.PARTIAL_MATCH_FOUND + " " + movie.Title + " (" + movie.Year + ") %" +
                               returnObject.pctLikelyMatch);
                    returnValue.Add(returnObject);
                }
            }
            if (returnValue.Count <= maxReturns)
            {
                logger.Log(strings.ExitingSearchForMediaByName + "(" + name + "," + maxReturns + ")");
                return (returnValue);
            }

            returnValue.Sort();
            returnValue.RemoveRange(maxReturns, returnValue.Count - maxReturns);
            logger.Log(strings.ExitingSearchForMediaByName + "(" + name + "," + maxReturns + ")");
            return (returnValue);
        }

        public override List<MediaSearchReturn> SearchForMediaByNameAndYear(string name, int year)
        {
            logger.Log(strings.EnteringSearchForMediaByNameAndYear + "(" + name + "," + year + ")");

            // Since we are searching for name already, we can get our list from that method
            // and just add 10 to the score if the year is an exact match
            List<MediaSearchReturn> returnValue = SearchForMediaByName(name);
            foreach (MediaSearchReturn mediaObject in returnValue)
            {
                if (mediaObject.year == year)
                {
                    mediaObject.pctLikelyMatch += 10;
                    if (mediaObject.pctLikelyMatch > 100)
                    {
                        mediaObject.pctLikelyMatch = 100;
                    }
                }
            }
            logger.Log(strings.ExitingSearchForMediaByNameAndYear + "(" + name + "," + year + ")");
            return (returnValue);
        }

        public override List<MediaSearchReturn> SearchForMediaByNameAndYear(string name, int year, int maxReturns)
        {
            logger.Log(strings.EnteringSearchForMediaByNameAndYear + "(" + name + "," + year + "," + maxReturns + ")");

            // Same thing here as SearchForMediaByNameAndYear, we can get all the returns from SearchForMediaByName
            // add 10 to any that have the same year, and then return maxReturns (or less) returns
            List<MediaSearchReturn> returnValue = SearchForMediaByName(name);
            foreach (MediaSearchReturn mediaObject in returnValue)
            {
                if (mediaObject.year == year)
                {
                    mediaObject.pctLikelyMatch += 10;
                    if (mediaObject.pctLikelyMatch > 100)
                    {
                        mediaObject.pctLikelyMatch = 100;
                    }
                }
            }
            if (returnValue.Count <= maxReturns)
            {
                logger.Log(strings.ExitingSearchForMediaByNameAndYear + "(" + name + "," + year + "," + maxReturns + ")");
                return (returnValue);
            }
            returnValue.Sort();
            returnValue.RemoveRange(maxReturns, returnValue.Count - maxReturns);
            logger.Log(strings.ExitingSearchForMediaByNameAndYear + "(" + name + "," + year + "," + maxReturns + ")");
            return (returnValue);
        }

        public override void SetDefaultConfig()
        {
            throw new NotImplementedException();
        }

        private void findMovieWrapper(string title)
        {
            waitingForFoundMovies = false;
            waitingForMovieParsed = false;
            imdbAPI.FindMovie(title);
            int a = 0;
            while (waitingForFoundMovies == false && a < 40 && waitingForMovieParsed == false)
            {
                Thread.Sleep(250);
                a++;
            }
            waitingForFoundMovies = false;
        }

        private void foundMovies(MoviesResultset Mov)
        {
            waitingForFoundMovies = true;
            foundMoviesResultSet = Mov;
        }

        private void getMovies(string title)
        {
            findMovieWrapper(title);
        }

        private void movieParsed(Movie M)
        {
            waitingForMovieParsed = true;
            parsedMovie = M;
        }

        #endregion Methods
    }
}